package com.um.jdy.mp.commission.manager;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.um.jdy.common.base.enums.YesOrNo;
import com.um.jdy.common.charge.service.OrderCommissionService;
import com.um.jdy.common.device.service.StationService;
import com.um.jdy.common.finance.entity.po.MerchantBill;
import com.um.jdy.common.finance.entity.po.MerchantCashout;
import com.um.jdy.common.finance.entity.po.PlatformBill;
import com.um.jdy.common.finance.entity.po.PlatformProfitBill;
import com.um.jdy.common.finance.enums.FinancialEnum;
import com.um.jdy.common.finance.service.MerchantBillService;
import com.um.jdy.common.finance.service.MerchantCashoutService;
import com.um.jdy.common.finance.service.PlatformBillService;
import com.um.jdy.common.finance.service.PlatformProfitBillService;
import com.um.jdy.common.member.enums.PayEnum;
import com.um.jdy.common.platform.code.PlatformCode;
import com.um.jdy.common.platform.entity.po.BankCard;
import com.um.jdy.common.platform.entity.po.Merchant;
import com.um.jdy.common.platform.entity.po.WithdrawSetting;
import com.um.jdy.common.platform.service.BankCardService;
import com.um.jdy.common.platform.service.MerchantService;
import com.um.jdy.common.platform.service.WithdrawSettingService;
import com.um.jdy.mp.commission.entity.dto.CashoutBankDTO;
import com.um.jdy.mp.commission.entity.vo.*;
import com.um.jdy.common.commons.manager.WxManager;
import com.um.jdy.mp.user.service.SessionUserService;
import com.um.springboot.starter.code.CommonCode;
import com.um.springboot.starter.entity.Page;
import com.um.springboot.starter.exception.ServiceException;
import com.um.springboot.starter.orm.entity.BaseVO;
import com.um.springboot.starter.orm.manager.BaseManager;
import com.um.springboot.starter.orm.params.PageParams;
import com.um.springboot.starter.utils.PayUtil;
import com.um.springboot.starter.utils.StringUtil;
import com.um.weixin.pay.service.WxTransferService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;

@Component
@Slf4j
public class CommissionManager extends BaseManager<OrderCommissionService> {

    @Autowired
    private SessionUserService sessionUserService;

    @Autowired
    private MerchantCashoutService merchantCashoutService;

    @Autowired
    private MerchantBillService merchantBillService;

    @Autowired
    private PlatformBillService platformBillService;

    @Autowired
    private PlatformProfitBillService platformProfitBillService;

    @Autowired
    private WithdrawSettingService withdrawSettingService;

    @Autowired
    private MerchantService merchantService;

    @Autowired
    StationService stationService;

    @Autowired
    WxTransferService wxTransferService;

    @Autowired
    WxManager wxManager;

    @Autowired
    BankCardService bankCardService;

    public StatisticsVO getStatistics() {
        String accountId = sessionUserService.getTokenUserId();

        //佣金累计
        Integer totalCommission = getTotalCommission(accountId);

        //总余额
        Integer totalBalance = getTotalBalance(accountId);

        //累计提现（提现金额）
        Integer totalCashout = getTotalCashout(accountId);

        //可提现（累计收益-累计提现-提现待审核金额）
        Integer pendingCashoutAmount = getWaitingCashout(accountId);

        Integer canCashout = NumberUtil.sub(totalBalance, pendingCashoutAmount).intValue();

        DateTime today = DateUtil.date();
        //今日佣金
        Integer todayCommission = getCommission(DateUtil.beginOfDay(today).toString(), DateUtil.endOfDay(today).toString(), accountId);
        //本周佣金
        Integer weekCommission = getCommission(DateUtil.beginOfWeek(today).toString(), DateUtil.endOfWeek(today).toString(), accountId);
        //本月佣金
        Integer monthCommission = getCommission(DateUtil.beginOfMonth(today).toString(), DateUtil.endOfMonth(today).toString(), accountId);
        //本年佣金
        Integer yearCommission = getCommission(DateUtil.beginOfYear(today).toString(), DateUtil.endOfYear(today).toString(), accountId);

        WithdrawSetting cashoutSetting = this.getCashoutSetting();
        StatisticsVO statisticsVO = new StatisticsVO();
        statisticsVO.setTotalIncome(totalCommission);
        statisticsVO.setTotalCashout(totalCashout);
        statisticsVO.setCanCashout(canCashout);
        statisticsVO.setTodayCommission(todayCommission);
        statisticsVO.setWeekCommission(weekCommission);
        statisticsVO.setMonthCommission(monthCommission);
        statisticsVO.setYearCommission(yearCommission);
        statisticsVO.setCashoutSetting(BeanUtil.toBean(cashoutSetting, WithdrawSettingVO.class));
        statisticsVO.setCashoutEnable(cashoutSetting.getStatus());

        return statisticsVO;
    }

    public Page getOrderList(String month, int pageNo, int pageSize) {
        Page<CommissionOrderVO> page = new Page<>();
        page.setPageNo(pageNo);
        page.setPageSize(pageSize);

        PageParams pageParams = new PageParams();

        if(!StringUtil.isEmpty(month)) {
            month = month.replace(".", "-") + "%";
        }else{
            month = "%";
        }

        page = service.getCMSViewModelPageBySqlName(page, "get_order_commission_page", "get_order_commission_count", CommissionOrderVO.class, pageParams.getProperties(), pageParams.getValues(),
                pageParams.getRangeEntitys(), pageParams.getOrderNames(), pageParams.getSortOns(), new Object[]{ sessionUserService.getTokenUser().getType(), getComId(), sessionUserService.getTokenUserId(), month}, false);
        return page;
    }

    /**
     * 提现记录
     *
     * @param lastYears  上个年月
     * @param pageParams 状态
     * @return
     */
    public Page<BaseVO> getCashoutPage(String lastYears, PageParams pageParams) {

        Page<MerchantCashoutItemVO> page = new Page();
        page.setPageNo(pageParams.getPageNo());
        page.setPageSize(pageParams.getPageSize());
        pageParams.getCondition().put("com_id", getComId());
        pageParams.getCondition().put("platform_merchant_id", sessionUserService.getTokenUserId());

        page = service.getCMSViewModelPageBySqlName(page, "get_cashout_records_page", "get_cashout_records_count", MerchantCashoutItemVO.class, pageParams.getProperties(), pageParams.getValues(),
                pageParams.getRangeEntitys(), pageParams.getOrderNames(), pageParams.getSortOns());

        Page<BaseVO> page2 = new Page();
        List<BaseVO> result = new ArrayList();
        for (MerchantCashoutItemVO item : page.getResult()) {
            String years = StringUtil.getTrim(item.getYears());
            if (!years.equals(lastYears) && !years.equals("")) {
                TitleTypeVO titleTypeVO = new TitleTypeVO();
                titleTypeVO.setYears(years);
                titleTypeVO.setTitleType("years");
                result.add(titleTypeVO);
                lastYears = years;
            }
            item.setTitleType("cashout");
            result.add(item);
        }

        page2.setTotalCount(page.getTotalCount());
        page2.setPageNo(pageParams.getPageNo());
        page2.setPageSize(pageParams.getPageSize());

        page2.setResult(result);
        return page2;
    }

    /**
     * 查询可提现金额
     *
     * @return
     */
    public Integer getCanCashout() {
        return this.getCanCashout(sessionUserService.getTokenUserId());
    }

    /**
     * 查询提现设置
     *
     * @return
     */
    public WithdrawSetting getCashoutSetting() {
        return withdrawSettingService.getEntity(new String[]{"com_id", "is_delete"}, new String[]{getComId(), "0"});
    }

    /**
     * 查询可提现金额
     *
     * @return
     */
    public Integer getCanCashout(String accountId) {

        //总余额
        Integer totalBalance = getTotalBalance(accountId);
        //可提现（累计收益-累计提现-提现待审核金额）
        Integer pendingCashoutAmount = getWaitingCashout(accountId);
        Integer canCashout = NumberUtil.sub(totalBalance, pendingCashoutAmount).intValue();
        return canCashout;
    }

    /**
     * 银行卡提现
     *
     * @param cashoutBankDTO
     */
    public void cashoutBank(CashoutBankDTO cashoutBankDTO) {

        checkPermission(cashoutBankDTO.getBaseBankId());

        //Integer amount = NumberUtil.round(StringUtil.toDouble(cashoutBankDTO.getAmount()) * 100, 0, RoundingMode.UP).intValue();
        cashout(cashoutBankDTO.getAmount(), PayEnum.CashOutType.Bank.name(), cashoutBankDTO.getBaseBankId());
    }

    /**
     * 微信提现
     *
     * @param amount
     */
    public void cashoutWX(Integer amount) {
        //amount = NumberUtil.round(StringUtil.toDouble(amount) * 100, 0, RoundingMode.UP).intValue();
        cashout(amount, PayEnum.CashOutType.Wxpay.name(), "");
    }

    /**
     * 提现
     *
     * @param amount
     */
    @Transactional
    public void cashout(Integer amount, String type, String bankCardId) {
        Merchant account = sessionUserService.getTokenUser();

        //校验提现设置
        WithdrawSetting cashoutSetting = this.getCashoutSetting();
        if (ObjectUtil.isNull(cashoutSetting)) {
            throw new ServiceException(PlatformCode.CashoutDisable);
        }
        if (!StringUtil.equals(cashoutSetting.getStatus(), YesOrNo.Yes.name())) {
            throw new ServiceException(PlatformCode.CashoutDisable);
        }

        if (type.equals(PayEnum.CashOutType.Bank.name()) && !StringUtil.equals(cashoutSetting.getBank(), YesOrNo.Yes.name())) {
            throw new ServiceException(PlatformCode.BankCashoutDisable);
        }
        if (type.equals(PayEnum.CashOutType.Wxpay.name()) && !StringUtil.equals(cashoutSetting.getWechat(), YesOrNo.Yes.name())) {
            throw new ServiceException(PlatformCode.WechatCashoutDisable);
        }

        if (amount > cashoutSetting.getSingleMax()) {
            throw new ServiceException(PlatformCode.ExceedSingleMaximum);
        }


        Integer todayCashoutTotalAmount = getTodayCashout(account.getPlatformMerchantId());
        if (todayCashoutTotalAmount + amount > cashoutSetting.getDayMax()) {
            throw new ServiceException(PlatformCode.ExceedDayMaximum);
        }

        //提现次数

        //校验提现金额
        Integer canCashout = this.getCanCashout();
        if (canCashout < amount) {
            throw new ServiceException(PlatformCode.NoBalance);
        }

        Integer handFee = (int) (amount * StringUtil.toDouble(cashoutSetting.getHandFee()) / 100);
        //保存提现提现申请
        MerchantCashout merchantCashout = new MerchantCashout();
        merchantCashout.setComId(account.getComId());
        merchantCashout.setAmount(amount);
        merchantCashout.setHandFee(handFee);
        merchantCashout.setMemberUserId(sessionUserService.getTokenUserId()); //申请用户id
        merchantCashout.setStatus(FinancialEnum.CashoutStatus.Pending.name());
        merchantCashout.setCreateTime(new Date());
        merchantCashout.setType(type);
        merchantCashout.setPlatformMerchantId(sessionUserService.getTokenUserId());

        if (!bankCardId.isEmpty() && PayEnum.CashOutType.Bank.name().equals(type)) {
            merchantCashout.setPlatformBankCardId(bankCardId);
        }

        merchantCashoutService.saveEntity(merchantCashout);

        //校验是否需要自动审核通过
        if (ObjectUtil.isNotEmpty(account) && StringUtil.equals(account.getCashoutAuditType(), FinancialEnum.CashoutAuditType.Auto.name())) {
            try {
                String cashoutId = StringUtil.getTrim(merchantCashout.getFinanceMerchantCashoutId());
                this.cashoutPass(account, cashoutId, type);
            } catch (Exception e) {
                log.error("自动审批提现失败，失败原因：", e);
            }
        }
    }

    /**
     * 提现审批通过
     *
     * @param cashoutId
     * @return
     */
    public void cashoutPass(Merchant account, String cashoutId, String payType) throws Exception {
        //银行卡的返回
        if (payType.equals(PayEnum.CashOutType.Bank.name())) {
            return;
        }
        //修改提现状态
        MerchantCashout cashout = merchantCashoutService.getEntity("finance_merchant_cashout_id", cashoutId);
        if (null == cashout) {
            throw new ServiceException(PlatformCode.NoCashout);
        }

        if (!StringUtil.equals(cashout.getStatus(), FinancialEnum.CashoutStatus.Pending.name())) {
            throw new ServiceException(PlatformCode.CashoutApproved);
        }

        Merchant merchant = sessionUserService.getTokenUser();
        String outTradeNo = PayUtil.getOutTradeNo();
        int amount = StringUtil.toInt(cashout.getAmount());
        int handFee = StringUtil.toInt(cashout.getHandFee());
        amount = amount - handFee;//提现金额-手续费

        //更新提现状态
        cashout.setStatus(FinancialEnum.CashoutStatus.Passed.name());
        cashout.setHandFee(handFee);
        cashout.setApproveTime(new Date());
        cashout.setApprover("system");//自动审核
        cashout.setOutTradeNo(outTradeNo);

        if (!PayUtil.isDev()) {
            if (payType.equals(PayEnum.CashOutType.Wxpay.name())) {
                try {
                    //微信提现
                    wxManager.getPayService(merchant.getComId());
                    wxTransferService.transfer(merchant.getMpOpenId(), outTradeNo, PayUtil.toTotalFee(amount), "提现");
                } catch (WxPayException e) {
                    log.info("商家转账给用户错误-{}", e.newBuilder().buildErrorMsg());
                    throw new ServiceException(e.getErrCodeDes());
                }
                cashout.setStatus(FinancialEnum.CashoutStatus.PendingLoan.name());
            } else if (payType.equals(PayEnum.CashOutType.Alipay.name())) {
                // 支付宝提现
            }
        }

        merchantCashoutService.save(cashout);

        String status = PayEnum.CashOutType.Wxpay.name().equals(cashout.getType()) ?
                FinancialEnum.BillStatus.NotEffective.name() : FinancialEnum.BillStatus.Effective.name();

        MerchantBill merchantBill = new MerchantBill();
        merchantBill.setComId(cashout.getComId());
        merchantBill.setPlatformMerchantId(account.getPlatformMerchantId());
        merchantBill.setOutTradeNo(outTradeNo);
        merchantBill.setAmount(amount);
        merchantBill.setCreateTime(new Date());
        merchantBill.setType(FinancialEnum.IncomeOrExpend.Expend.name());
        merchantBill.setStatus(status);
        merchantBill.setEvent(FinancialEnum.MerchantBillEvent.Cashout.name());
        merchantBill.setNo(StrUtil.builder(DateUtil.date().toString(DatePattern.PURE_DATETIME_MS_PATTERN), RandomUtil.randomNumbers(4)).toString());
        merchantBillService.save(merchantBill);

        MerchantBill merchantBill1 = new MerchantBill();
        merchantBill1.setComId(cashout.getComId());
        merchantBill1.setPlatformMerchantId(account.getPlatformMerchantId());
        merchantBill1.setOutTradeNo(outTradeNo);
        merchantBill1.setAmount(handFee);
        merchantBill1.setCreateTime(new Date());
        merchantBill1.setType(FinancialEnum.IncomeOrExpend.Expend.name());
        merchantBill1.setStatus(status);
        merchantBill1.setEvent(FinancialEnum.MerchantBillEvent.HandFee.name());
        merchantBill1.setNo(StrUtil.builder(DateUtil.date().toString(DatePattern.PURE_DATETIME_MS_PATTERN), RandomUtil.randomNumbers(4)).toString());
        merchantBillService.save(merchantBill1);

        //添加提现流水
        PlatformBill platformBill = new PlatformBill();
        platformBill.setOutTradeNo(outTradeNo);
        platformBill.setAmount(amount);
        platformBill.setCreateTime(new Date());
        platformBill.setType(FinancialEnum.IncomeOrExpend.Expend.name());
        platformBill.setStatus(status);
        platformBill.setEvent(FinancialEnum.PlatformBillEvent.Cashout.name());
        platformBill.setNo(StrUtil.builder(DateUtil.date().toString(DatePattern.PURE_DATETIME_MS_PATTERN), RandomUtil.randomNumbers(4)).toString());
        platformBillService.save(platformBill);

        //添加手续费流水
        PlatformProfitBill platformProfitBill = new PlatformProfitBill();
        platformProfitBill.setOutTradeNo(outTradeNo);
        platformProfitBill.setAmount(handFee);
        platformProfitBill.setCreateTime(new Date());
        platformProfitBill.setType(FinancialEnum.IncomeOrExpend.Income.name());
        platformProfitBill.setStatus(status);
        platformProfitBill.setEvent(FinancialEnum.PlatformBillEvent.HandFee.name());
        platformProfitBill.setNo(StrUtil.builder(DateUtil.date().toString(DatePattern.PURE_DATETIME_MS_PATTERN), RandomUtil.randomNumbers(4)).toString());
        platformProfitBillService.save(platformProfitBill);
    }


    /**
     * 提现审核中金额
     *
     * @return
     */
    private Integer getWaitingCashout(String accountId) {
        Long income = service.getCountBySqlName("merchant_get_waiting_cash_out", new Object[]{accountId, getComId()});
        return income.intValue();
    }

    /**
     * 查询当前提现金额（审核中+已通过）
     *
     * @return
     */
    private Integer getTodayCashout(String accountId) {
        Long income = service.getCountBySqlName("merchant_get_today_total_cash_out", new Object[]{accountId, getComId(),DateUtil.format(new Date(),"yyyy-MM-dd")});
        return income.intValue();
    }


    /**
     * 总余额(包括提现待审核的金额)
     *
     * @return
     */
    private Integer getTotalBalance(String accountId) {
        return getTotalIncome(accountId) - getTotalExpend(accountId);
    }

    /**
     * 累计收入
     *
     * @return
     */
    private Integer getTotalIncome(String accountId) {
        Long income = service.getCountBySqlName("merchant_get_total_income", new Object[]{accountId, getComId()});
        return income.intValue();
    }

    /**
     * 累计支出
     *
     * @return
     */
    private Integer getTotalExpend(String accountId) {
        Long income = service.getCountBySqlName("merchant_get_total_expend", new Object[]{accountId, getComId()});
        return income.intValue();
    }

    /**
     * 累计佣金收入
     * @param accountId
     * @return
     */
    private Integer getTotalCommission(String accountId){
        Long total = service.getCountBySqlName("merchant_get_total_commission",new Object[]{accountId, getComId()});
        return total.intValue();
    }

    /**
     * 累计提现（提现金额）
     *
     * @return
     */
    private Integer getTotalCashout(String accountId) {
        Long income = service.getCountBySqlName("merchant_get_total_cash_out", new Object[]{accountId, getComId()});
        return income.intValue();
    }

    /**
     * 根据日期返回收益
     *
     * @param beginDate 开始日期
     * @param endDate   结束日期
     * @return
     */
    private Integer getCommission(String beginDate, String endDate, String accountId) {
        Long income = service.getCountBySqlName("merchant_get_datetime_commission",
                new Object[]{accountId, getComId(), beginDate + " 00:00:00", endDate + " 23:59:59"});
        return income.intValue();
    }

    /**
     * 统计累计订单及收益流水
     * @return
     */
    public CommissionSummaryVO getCommissionSummary(){
        String accountId = sessionUserService.getTokenUserId();
        String datetime = DateUtil.format(new Date(),"yyyy-MM-dd")+" 23:59:59";
        CommissionSummaryVO orderCommissionVO = service.getViewModel("get_commission_order_summary", CommissionSummaryVO.class, new Object[]{getComId(),accountId,datetime});
        CommissionSummaryVO billCommissionVO = service.getViewModel("get_commission_bill_summary", CommissionSummaryVO.class, new Object[]{getComId(),accountId,datetime});

        orderCommissionVO.setCommission(billCommissionVO.getCommission());

        return orderCommissionVO;
    }

    /**
     * 代理商、运营商、商户流水查询
     * @return
     */
    public CommissionListSummaryVO getCommissionSummaryList(String id, String type,String year_or_month) {

        //log.info("id:{},type:{},year_or_month:{}",id,type,year_or_month);
        List<CommissionSummaryItemVO> list = new ArrayList<>();
        int totalOrders = 0;
        int totalCommission = 0;

        if(type.equals("year")){
            List<CommissionSummaryItemVO> orders = service.getViewModelList("get_commission_order_summary_by_year",CommissionSummaryItemVO.class,new Object[]{getComId(),id,year_or_month});
            List<CommissionSummaryItemVO> bills = service.getViewModelList("get_commission_bill_summary_by_year",CommissionSummaryItemVO.class,new Object[]{getComId(),id,year_or_month});

            for(int index = 1;index <= 12;index ++){
                String month = year_or_month.trim()+"-"+(index < 10 ? "0"+index : index);

                CommissionSummaryItemVO item = new CommissionSummaryItemVO();
                item.setMonthOrDate(month);
                item.setCommission(0);
                item.setOrderNum(0);

                for(CommissionSummaryItemVO orderItem : orders){
                    if(orderItem.getMonthOrDate().equals(month)){
                        item.setOrderNum(orderItem.getOrderNum());
                        totalOrders += orderItem.getOrderNum();
                        break;
                    }
                }

                for(CommissionSummaryItemVO billItem : bills){
                    if(billItem.getMonthOrDate().equals(month)){
                        item.setCommission(billItem.getCommission());
                        totalCommission += billItem.getCommission();
                        break;
                    }
                }

                list.add(item);
            }
        }else if(type.equals("month")){
            List<CommissionSummaryItemVO> orders = service.getViewModelList("get_commission_order_summary_by_month",CommissionSummaryItemVO.class,new Object[]{getComId(),id,year_or_month});
            List<CommissionSummaryItemVO> bills = service.getViewModelList("get_commission_bill_summary_by_month",CommissionSummaryItemVO.class,new Object[]{getComId(),id,year_or_month});

            String[] arr = StringUtils.split(year_or_month,"-");
            int days = com.um.springboot.starter.utils.DateUtil.checkMonth(StringUtil.toInt(arr[1]),StringUtil.toInt(arr[0]));
            //log.info("days:{},arr:{}",days,arr);
            for(int index = 1;index <= days;index ++){
                String date = year_or_month.trim()+"-"+(index < 10 ? "0"+index : index);

                CommissionSummaryItemVO item = new CommissionSummaryItemVO();
                item.setMonthOrDate(date);
                item.setCommission(0);
                item.setOrderNum(0);

                for(CommissionSummaryItemVO orderItem : orders){
                    if(orderItem.getMonthOrDate().equals(date)){
                        item.setOrderNum(orderItem.getOrderNum());
                        totalOrders += orderItem.getOrderNum();
                        break;
                    }
                }

                for(CommissionSummaryItemVO billItem : bills){
                    if(billItem.getMonthOrDate().equals(date)){
                        item.setCommission(billItem.getCommission());
                        totalCommission += billItem.getCommission();
                        break;
                    }
                }

                list.add(item);
            }
        }

        CommissionListSummaryVO summaryVO = new CommissionListSummaryVO();
        summaryVO.setCommission(totalCommission);
        summaryVO.setOrderNum(totalOrders);
        summaryVO.setList(list);

        if(!id.equals(sessionUserService.getTokenUserId())){
            Merchant merchant = merchantService.getEntity(id);
            if(merchant != null){
                summaryVO.setName(merchant.getName());
                summaryVO.setLogo(merchant.getLogo());
            }
        }

        return summaryVO;
    }

    /**
     * 校验数据权限
     * @param id
     */
    private void checkPermission(String id){
        BankCard entity = bankCardService.getEntity(id);
        if(entity == null)
            throw new ServiceException(CommonCode.DataNotExist);

        if(!StringUtil.equals(sessionUserService.getTokenUserId(),entity.getUserId()))
            throw new ServiceException(CommonCode.NoDataPermission);
    }
}
